package com.dc.schedule.spring.task;

import com.dc.schedule.client.context.ClientExecutionContextManager;
import com.dc.schedule.client.context.ClientTaskExecutionContext;
import org.springframework.scheduling.support.ScheduledMethodRunnable;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.function.Supplier;

public class ContextualMethodInjectionTaskRunnable extends ScheduledMethodRunnable {
    private final Supplier<Object[]> parameterSupplier;
    
    public ContextualMethodInjectionTaskRunnable(Object target, Method method) {
        super(target, method);
        parameterSupplier = analysisMethod();
    }
    
    @Override
    public void run() {
        try {
            ReflectionUtils.makeAccessible(getMethod());
            final Object result = this.getMethod().invoke(getTarget(), parameterSupplier.get());
            ClientTaskExecutionContext context = ClientExecutionContextManager.getINSTANCE().getCurrentExecutionContext();
            if (!(Void.class.isAssignableFrom(getMethod().getReturnType())
                          || void.class.isAssignableFrom(getMethod().getReturnType()))) {
                context.setResultMessage(String.valueOf(result));
            }
        } catch (InvocationTargetException ex) {
            ReflectionUtils.rethrowRuntimeException(ex.getTargetException());
        } catch (IllegalAccessException ex) {
            throw new UndeclaredThrowableException(ex);
        }
    }
    
    private Supplier<Object[]> analysisMethod() {
        final Class<?>[] types = getMethod().getParameterTypes();
        for (Class<?> type : types) {
            if (!ClientTaskExecutionContext.class.isAssignableFrom(type)) {
                throw new IllegalStateException("调度任务方法" + getMethod() + "参数不被支持, 目前只支持没有参数或者一个 ClientTaskExecutionContext 类型的参数");
            }
        }
        if (types.length == 0) {
            return () -> new Object[]{};
        } else {
            return () -> new Object[]{
                    ClientExecutionContextManager.getINSTANCE().getCurrentExecutionContext()
            };
        }
    }
}
